/**
 *  memory controll utils for linux
 *  @author hrz
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DEFAULT_MEMORY_BLOCK_SIZE 10485760
#define DEFAULT_BLOCK_ARRAY_LENGTH 10240
#define MEMINFO_FILE "/proc/meminfo"
int firstLoad = 0;
int desPercent = 0;
int *ptrBlockArray[DEFAULT_BLOCK_ARRAY_LENGTH] = {NULL};
int needExit = 0;
int refresh_hz = 0;

struct MemInfo {
    unsigned long long total;
    unsigned long long free;
};

int main(int argc, char *argv[], char *envp[]) {
    printf("########### Pre-Start.. please wait #################\n");
    initPercent(argv[1]);
    initRate(argv[2]);
    size_t memSize = sizeof(struct MemInfo);//*online_cpu;
    struct MemInfo *memInfo = malloc(memSize);
    memset(memInfo,0,memSize);
    printf("########### Started #################\n");
    while (needExit == 0) {
        initMemoryInfo(memInfo);
        changeUsedMemory(memInfo);
        sleep(refresh_hz);
    }
    return  0;
}


/**
 * 输入 期望内存占比 范围(10,80)
 * @return percent 合法输入
 */
void initPercent(char *s) {
    int iPercent = atoi(s);
    printf("using percent:%d\n", iPercent);
    if (iPercent < 10 || iPercent > 80) {
        printf("invalid value , system exit\n");
        exit(0);
    }
    desPercent = iPercent;
}

void initRate(char *s) {
    int iRate = atoi(s);
    printf("using rate:%d\n", iRate);
    if (iRate < 6 || iRate > 99) {
        printf("invalid value, the  is  rate not in range (5,100) , system exit\n");
        exit(0);
    }
    refresh_hz = iRate; // million seconds
}


void initMemoryInfo(struct MemInfo *memInfo) {
    printf("begin get memory info\n");
    // clean
    memInfo->total = 0;
    memInfo->free = 0;
    //read from /proc/meminfo
    FILE *fd;
    fd = fopen(MEMINFO_FILE, "r");
    char buffer[256];
    char name1[50];
    char name2[50];
    unsigned long long value = 0;
    unsigned long long MemTotal = 0;
    unsigned long long MemFree = 0;
    unsigned long long Buffers = 0;
    unsigned long long Cached = 0;
    unsigned long long SReclaimable = 0;
    while (fgets(buffer, sizeof(buffer), fd) != NULL) {
        sscanf(buffer, "%s %lld %s\n", name1, &value, name2);
        if (strncmp(name1, "MemTotal:", strlen("MemTotal:")) == 0) {
            MemTotal = value;
        } else if (strncmp(name1, "MemFree:", strlen("MemFree:")) == 0) {
            MemFree = value;
        } else if (strncmp(name1, "Buffers:", strlen("Buffers:")) == 0) {
            Buffers = value;
        } else if (strncmp(name1, "Cached:", strlen("Cached:")) == 0) {
            Cached = value;
        } else if (strncmp(name1, "SReclaimable:", strlen("SReclaimable:")) == 0) {
            SReclaimable = value;
        }
    }
    //free memory
    fclose(fd);
    memInfo->total = MemTotal / 1024; //to MB
    // debian eg ubuntu    可用内存=free的内存+cached的内存+buffers的内存
    // SUSE  eg centos  可用内存=free的内存+cached的内存+buffers的内存+SReclaimable的内存
    // view more http://www.cnblogs.com/black-mamba/p/5918688.html
    memInfo->free = (MemFree + Cached + Buffers + SReclaimable) / 1024; //to MB
    printf("get memory info successfully!total:%lld MB，free: %lld MB\n", memInfo->total, memInfo->free);
}


void changeUsedMemory(struct MemInfo *memInfo) {
    printf("begin refresh memory blocks  \n");
    unsigned long long usedPercent = (unsigned long long) (((memInfo->total - memInfo->free) * 1.0 / memInfo->total) *
                                                           100);
    if (usedPercent > desPercent) {
        //如果没有需要释放的内存则跳过
        if (firstLoad == 0) {
            firstLoad = 1;
            printf("current memory is %d is grater than  the destination percent %d,do nothing\n", usedPercent,
                   desPercent);
            return;
        }
        freeMemoryBlock(memInfo);
        //释放内存
        return;
    }
    addMemoryBlock(memInfo);
    //申请内存
    printf("end refresh memory blocks  \n");
}


void freeMemoryBlock(struct MemInfo *memInfo) {
    printf("begin free memory blocks\n");
    unsigned long long dist = (unsigned long long) (memInfo->total * desPercent * 0.01);
    //计算需要释放的块 = 当前占用 - 目标占用 = （总内存 - 可用内存) - 目标占用内存
    unsigned long long needToFreeBlockCount = ((memInfo->total - memInfo->free - dist)) / 10;
    printf("need to free blocks: %lld\n", needToFreeBlockCount);
    if (needToFreeBlockCount < 0) {
        printf("end free memory blocks with exception \n");
        return;
    }
    long freeCount = 0;
    for (int i = 0; i < DEFAULT_BLOCK_ARRAY_LENGTH; i++) {
        if (needToFreeBlockCount < 1) {
            break;
        }
        if (ptrBlockArray[i] != NULL) {
            //指向默认指针
            free(ptrBlockArray[i]);
            ptrBlockArray[i] = NULL;
            needToFreeBlockCount = needToFreeBlockCount - 1;
            freeCount = freeCount + 1;
        }
    }
    printf("finally %ld blocks had been free\n", freeCount);
}


void addMemoryBlock(struct MemInfo *memInfo) {
    printf("begin add memory blocks\n");
    //计算 需要增加的内存 = 目标占用内存 - 已经占用内存 = 目标占用内存 -（总内存-可用内存）
    unsigned long long needToAddBlockCount = (unsigned long long)
            ((memInfo->total * desPercent * 0.01 - memInfo->total + memInfo->free) / 10);
    printf("need to add blocks: %lld\n", needToAddBlockCount);
    if (needToAddBlockCount < 0) {
        printf("end add memory blocks with exception \n");
        return;
    }
    long added = 0;
    for (int i = 0; i < DEFAULT_BLOCK_ARRAY_LENGTH; i++) {
        //分配内存数组
        if (needToAddBlockCount < 1) {
            break;
        }
        //指向默认，则是未分配状态
        if (ptrBlockArray[i] == NULL) {
            ptrBlockArray[i] = (int *) malloc(DEFAULT_MEMORY_BLOCK_SIZE);
            if (ptrBlockArray[i] == NULL) {
                //没有可分配内存，退出
                break;
            }
            memset(ptrBlockArray[i],0,DEFAULT_MEMORY_BLOCK_SIZE);
            needToAddBlockCount = needToAddBlockCount - 1;
            added = added + 1;
        }
    }
    printf("finally %ld blocks had been added\n", added);
}